ace.define(
  'ace/mode/coffee_highlight_rules',
  ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text_highlight_rules'],
  function (require, exports, module) {
    'use strict';
    var oop = require('../lib/oop');
    var TextHighlightRules = require('./text_highlight_rules').TextHighlightRules;
    oop.inherits(CoffeeHighlightRules, TextHighlightRules);
    function CoffeeHighlightRules() {
      var identifier = '[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*';
      var keywords =
        'this|throw|then|try|typeof|super|switch|return|break|by|continue|' +
        'catch|class|in|instanceof|is|isnt|if|else|extends|for|own|' +
        'finally|function|while|when|new|no|not|delete|debugger|do|loop|of|off|' +
        'or|on|unless|until|and|yes|yield|export|import|default';
      var langConstant = 'true|false|null|undefined|NaN|Infinity';
      var illegal =
        'case|const|function|var|void|with|enum|implements|' + 'interface|let|package|private|protected|public|static';
      var supportClass =
        'Array|Boolean|Date|Function|Number|Object|RegExp|ReferenceError|String|' +
        'Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|' +
        'SyntaxError|TypeError|URIError|' +
        'ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|' +
        'Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray';
      var supportFunction =
        'Math|JSON|isNaN|isFinite|parseInt|parseFloat|encodeURI|' +
        'encodeURIComponent|decodeURI|decodeURIComponent|String|';
      var variableLanguage = 'window|arguments|prototype|document';
      var keywordMapper = this.createKeywordMapper(
        {
          keyword: keywords,
          'constant.language': langConstant,
          'invalid.illegal': illegal,
          'language.support.class': supportClass,
          'language.support.function': supportFunction,
          'variable.language': variableLanguage,
        },
        'identifier',
      );
      var functionRule = {
        token: ['paren.lparen', 'variable.parameter', 'paren.rparen', 'text', 'storage.type'],
        regex: /(?:(\()((?:"[^")]*?"|'[^')]*?'|\/[^\/)]*?\/|[^()"'\/])*?)(\))(\s*))?([\-=]>)/.source,
      };
      var stringEscape = /\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)/;
      this.$rules = {
        start: [
          {
            token: 'constant.numeric',
            regex: '(?:0x[\\da-fA-F]+|(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:[eE][+-]?\\d+)?)',
          },
          {
            stateName: 'qdoc',
            token: 'string',
            regex: "'''",
            next: [
              { token: 'string', regex: "'''", next: 'start' },
              { token: 'constant.language.escape', regex: stringEscape },
              { defaultToken: 'string' },
            ],
          },
          {
            stateName: 'qqdoc',
            token: 'string',
            regex: '"""',
            next: [
              { token: 'string', regex: '"""', next: 'start' },
              { token: 'paren.string', regex: '#{', push: 'start' },
              { token: 'constant.language.escape', regex: stringEscape },
              { defaultToken: 'string' },
            ],
          },
          {
            stateName: 'qstring',
            token: 'string',
            regex: "'",
            next: [
              { token: 'string', regex: "'", next: 'start' },
              { token: 'constant.language.escape', regex: stringEscape },
              { defaultToken: 'string' },
            ],
          },
          {
            stateName: 'qqstring',
            token: 'string.start',
            regex: '"',
            next: [
              { token: 'string.end', regex: '"', next: 'start' },
              { token: 'paren.string', regex: '#{', push: 'start' },
              { token: 'constant.language.escape', regex: stringEscape },
              { defaultToken: 'string' },
            ],
          },
          {
            stateName: 'js',
            token: 'string',
            regex: '`',
            next: [
              { token: 'string', regex: '`', next: 'start' },
              { token: 'constant.language.escape', regex: stringEscape },
              { defaultToken: 'string' },
            ],
          },
          {
            regex: '[{}]',
            onMatch: function (val, state, stack) {
              this.next = '';
              if (val == '{' && stack.length) {
                stack.unshift('start', state);
                return 'paren';
              }
              if (val == '}' && stack.length) {
                stack.shift();
                this.next = stack.shift() || '';
                if (this.next.indexOf('string') != -1) return 'paren.string';
              }
              return 'paren';
            },
          },
          {
            token: 'string.regex',
            regex: '///',
            next: 'heregex',
          },
          {
            token: 'string.regex',
            regex:
              /(?:\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)(?:[imgy]{0,4})(?!\w)/,
          },
          {
            token: 'comment',
            regex: '###(?!#)',
            next: 'comment',
          },
          {
            token: 'comment',
            regex: '#.*',
          },
          {
            token: ['punctuation.operator', 'text', 'identifier'],
            regex: '(\\.)(\\s*)(' + illegal + ')',
          },
          {
            token: 'punctuation.operator',
            regex: '\\.{1,3}',
          },
          {
            token: ['keyword', 'text', 'language.support.class', 'text', 'keyword', 'text', 'language.support.class'],
            regex: '(class)(\\s+)(' + identifier + ')(?:(\\s+)(extends)(\\s+)(' + identifier + '))?',
          },
          {
            token: ['entity.name.function', 'text', 'keyword.operator', 'text'].concat(functionRule.token),
            regex: '(' + identifier + ')(\\s*)([=:])(\\s*)' + functionRule.regex,
          },
          functionRule,
          {
            token: 'variable',
            regex: '@(?:' + identifier + ')?',
          },
          {
            token: keywordMapper,
            regex: identifier,
          },
          {
            token: 'punctuation.operator',
            regex: '\\,|\\.',
          },
          {
            token: 'storage.type',
            regex: '[\\-=]>',
          },
          {
            token: 'keyword.operator',
            regex: '(?:[-+*/%<>&|^!?=]=|>>>=?|\\-\\-|\\+\\+|::|&&=|\\|\\|=|<<=|>>=|\\?\\.|\\.{2,3}|[!*+-=><])',
          },
          {
            token: 'paren.lparen',
            regex: '[({[]',
          },
          {
            token: 'paren.rparen',
            regex: '[\\]})]',
          },
          {
            token: 'text',
            regex: '\\s+',
          },
        ],
        heregex: [
          {
            token: 'string.regex',
            regex: '.*?///[imgy]{0,4}',
            next: 'start',
          },
          {
            token: 'comment.regex',
            regex: '\\s+(?:#.*)?',
          },
          {
            token: 'string.regex',
            regex: '\\S+',
          },
        ],
        comment: [
          {
            token: 'comment',
            regex: '###',
            next: 'start',
          },
          {
            defaultToken: 'comment',
          },
        ],
      };
      this.normalizeRules();
    }
    exports.CoffeeHighlightRules = CoffeeHighlightRules;
  },
);

ace.define(
  'ace/mode/matching_brace_outdent',
  ['require', 'exports', 'module', 'ace/range'],
  function (require, exports, module) {
    'use strict';
    var Range = require('../range').Range;
    var MatchingBraceOutdent = function () {};
    (function () {
      this.checkOutdent = function (line, input) {
        if (!/^\s+$/.test(line)) return false;
        return /^\s*\}/.test(input);
      };
      this.autoOutdent = function (doc, row) {
        var line = doc.getLine(row);
        var match = line.match(/^(\s*\})/);
        if (!match) return 0;
        var column = match[1].length;
        var openBracePos = doc.findMatchingBracket({ row: row, column: column });
        if (!openBracePos || openBracePos.row == row) return 0;
        var indent = this.$getIndent(doc.getLine(openBracePos.row));
        doc.replace(new Range(row, 0, row, column - 1), indent);
      };
      this.$getIndent = function (line) {
        return line.match(/^\s*/)[0];
      };
    }.call(MatchingBraceOutdent.prototype));
    exports.MatchingBraceOutdent = MatchingBraceOutdent;
  },
);

ace.define(
  'ace/mode/folding/coffee',
  ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/folding/fold_mode', 'ace/range'],
  function (require, exports, module) {
    'use strict';
    var oop = require('../../lib/oop');
    var BaseFoldMode = require('./fold_mode').FoldMode;
    var Range = require('../../range').Range;
    var FoldMode = (exports.FoldMode = function () {});
    oop.inherits(FoldMode, BaseFoldMode);
    (function () {
      this.getFoldWidgetRange = function (session, foldStyle, row) {
        var range = this.indentationBlock(session, row);
        if (range) return range;
        var re = /\S/;
        var line = session.getLine(row);
        var startLevel = line.search(re);
        if (startLevel == -1 || line[startLevel] != '#') return;
        var startColumn = line.length;
        var maxRow = session.getLength();
        var startRow = row;
        var endRow = row;
        while (++row < maxRow) {
          line = session.getLine(row);
          var level = line.search(re);
          if (level == -1) continue;
          if (line[level] != '#') break;
          endRow = row;
        }
        if (endRow > startRow) {
          var endColumn = session.getLine(endRow).length;
          return new Range(startRow, startColumn, endRow, endColumn);
        }
      };
      this.getFoldWidget = function (session, foldStyle, row) {
        var line = session.getLine(row);
        var indent = line.search(/\S/);
        var next = session.getLine(row + 1);
        var prev = session.getLine(row - 1);
        var prevIndent = prev.search(/\S/);
        var nextIndent = next.search(/\S/);
        if (indent == -1) {
          session.foldWidgets[row - 1] = prevIndent != -1 && prevIndent < nextIndent ? 'start' : '';
          return '';
        }
        if (prevIndent == -1) {
          if (indent == nextIndent && line[indent] == '#' && next[indent] == '#') {
            session.foldWidgets[row - 1] = '';
            session.foldWidgets[row + 1] = '';
            return 'start';
          }
        } else if (prevIndent == indent && line[indent] == '#' && prev[indent] == '#') {
          if (session.getLine(row - 2).search(/\S/) == -1) {
            session.foldWidgets[row - 1] = 'start';
            session.foldWidgets[row + 1] = '';
            return '';
          }
        }
        if (prevIndent != -1 && prevIndent < indent) session.foldWidgets[row - 1] = 'start';
        else session.foldWidgets[row - 1] = '';
        if (indent < nextIndent) return 'start';
        else return '';
      };
    }.call(FoldMode.prototype));
  },
);

ace.define(
  'ace/mode/coffee',
  [
    'require',
    'exports',
    'module',
    'ace/mode/coffee_highlight_rules',
    'ace/mode/matching_brace_outdent',
    'ace/mode/folding/coffee',
    'ace/range',
    'ace/mode/text',
    'ace/worker/worker_client',
    'ace/lib/oop',
  ],
  function (require, exports, module) {
    'use strict';
    var Rules = require('./coffee_highlight_rules').CoffeeHighlightRules;
    var Outdent = require('./matching_brace_outdent').MatchingBraceOutdent;
    var FoldMode = require('./folding/coffee').FoldMode;
    var Range = require('../range').Range;
    var TextMode = require('./text').Mode;
    var WorkerClient = require('../worker/worker_client').WorkerClient;
    var oop = require('../lib/oop');
    function Mode() {
      this.HighlightRules = Rules;
      this.$outdent = new Outdent();
      this.foldingRules = new FoldMode();
    }
    oop.inherits(Mode, TextMode);
    (function () {
      var indenter =
        /(?:[({[=:]|[-=]>|\b(?:else|try|(?:swi|ca)tch(?:\s+[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)?|finally))\s*$|^\s*(else\b\s*)?(?:if|for|while|loop)\b(?!.*\bthen\b)/;
      this.lineCommentStart = '#';
      this.blockComment = { start: '###', end: '###' };
      this.getNextLineIndent = function (state, line, tab) {
        var indent = this.$getIndent(line);
        var tokens = this.getTokenizer().getLineTokens(line, state).tokens;
        if (
          !(tokens.length && tokens[tokens.length - 1].type === 'comment') &&
          state === 'start' &&
          indenter.test(line)
        )
          indent += tab;
        return indent;
      };
      this.checkOutdent = function (state, line, input) {
        return this.$outdent.checkOutdent(line, input);
      };
      this.autoOutdent = function (state, doc, row) {
        this.$outdent.autoOutdent(doc, row);
      };
      this.createWorker = function (session) {
        var worker = new WorkerClient(['ace'], 'ace/mode/coffee_worker', 'Worker');
        worker.attachToDocument(session.getDocument());
        worker.on('annotate', function (e) {
          session.setAnnotations(e.data);
        });
        worker.on('terminate', function () {
          session.clearAnnotations();
        });
        return worker;
      };
      this.$id = 'ace/mode/coffee';
      this.snippetFileId = 'ace/snippets/coffee';
    }.call(Mode.prototype));
    exports.Mode = Mode;
  },
);
(function () {
  ace.require(['ace/mode/coffee'], function (m) {
    if (typeof module == 'object' && typeof exports == 'object' && module) {
      module.exports = m;
    }
  });
})();
